package com.kakame.dibaaz.sieve;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.kakame.dibaaz.annotation.Module;
import com.kakame.dibaaz.annotation.ToView;
import com.kakame.dibaaz.annotation.handler.ToViewHandler;
import com.kakame.dibaaz.box.ReflectBox;
import com.kakame.dibaaz.box.WebBox;
import com.kakame.dibaaz.exception.MappingException;
import com.kakame.dibaaz.tool.AnnotationTool;
import com.kakame.dibaaz.tool.ReflectTool;
import com.kakame.dibaaz.tool.StringTool;

import net.sf.cglib.proxy.Enhancer;

public class DibaazSieve extends HttpServlet {
	private static final long serialVersionUID = 9188581003105686613L;

	/** 读取配置 */
	public static Properties SYSTEM_CONFIG_PRO = null;

	/** 读取全部的映射方法 */
	public static Map<String, ReflectBox> MAPPING_METHODS = new HashMap<>();

	public void init(ServletConfig config) throws ServletException {
		// 初始化读取配置文件
		if (SYSTEM_CONFIG_PRO == null) {
			String dibaazExplain = config.getInitParameter("dibaazExplain");
			if (StringTool.isNotBlank(dibaazExplain)) {
				SYSTEM_CONFIG_PRO = new Properties();
				InputStream in = this.getClass().getClassLoader().getResourceAsStream(dibaazExplain);
				try {
					SYSTEM_CONFIG_PRO.load(in);
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					if (in != null) {
						try {
							in.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}

			List<String> allMappings = new ArrayList<>(); // 临时用来存储所有的映射
			List<ReflectBox> methodEntitys = new ArrayList<>();
			// 读取所有的controller
			// 获取特定包下所有的类(包括接口和类)
			List<Class<?>> clsList = ReflectTool
					.getAllClassByPackageName(SYSTEM_CONFIG_PRO.getProperty("dibaaz.package.base"));
			// 输出所有使用了特定注解的类的注解值
			List<Class<?>> classes = AnnotationTool.getClassAnnotation(clsList, Module.class);
			for (Class<?> moduleClass : classes) {
				List<Method> methods = AnnotationTool.getMethodAnnotation(moduleClass, ToView.class);
				for (Method method : methods) {

					Module module = moduleClass.getAnnotation(Module.class);
					ToView toView = method.getAnnotation(ToView.class);

					ReflectBox entity = new ReflectBox();
					entity.setArguments(method.getParameterTypes());
					entity.setClazz(moduleClass);
					entity.setMethod(method);

					int urlLen = toView.url().length;
					if (urlLen > 0) {
						String[] urls = new String[urlLen];
						String moduleUrl = module.value();
						String[] methodUrls = toView.url();
						for (int i = 0; i < urlLen; i++) {
							if (StringTool.isNotBlank(methodUrls[i])) {
								urls[i] = moduleUrl + methodUrls[i];
								if (allMappings.contains(urls[i])) {
									throw new MappingException("存在重复的地址映射：" + urls[i]);
								}
								allMappings.add(urls[i]);
							} else {
								throw new MappingException("方法没有对应的映射地址：" + method.getDeclaringClass());
							}
						}
						entity.setUrls(urls);
					} else {
						throw new MappingException("方法没有对应的映射地址：" + method.getDeclaringClass());
					}

					entity.setResponseType(toView.responseType());
					entity.setView(toView.view());
					entity.setRequestType(toView.requestType());

					methodEntitys.add(entity);

					for (String url : entity.getUrls()) {
						MAPPING_METHODS.put(url, entity);
					}
				}
			}
		}
	}

	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		WebBox webBox = new WebBox();
		webBox.setRequest(request);
		webBox.setResponse(response);

		StringBuffer requestURL = request.getRequestURL(); // http://localhost:8080/kakame-hejek/hello/sayHello
		String contextPath = request.getContextPath(); // /kakame-hejek
		String requestURI = request.getRequestURI(); // /kakame-hejek/hello/sayHello
		String requestBody = requestURI.replaceFirst(contextPath, "").replaceAll("/+", "/"); // /hello/sayHello
		ReflectBox reflectEntity = MAPPING_METHODS.get(requestBody);

		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(reflectEntity.getClazz());
		enhancer.setCallback(new ToViewHandler());

		try {
			Object proxy = enhancer.create();
			Object cast = reflectEntity.getClazz().cast(proxy);
			Method method = cast.getClass().getMethod(reflectEntity.getMethod().getName(), WebBox.class);
			WebBox invoke = (WebBox) method.invoke(proxy, webBox);
			String responseUrl = invoke.getReturnResponseUrl();
			String requestUrl = invoke.getReturnRequestUrl();
			if (StringTool.isNotBlank(requestUrl)) {
				request.getRequestDispatcher(requestUrl).forward(request, response);
			} else if(StringTool.isNotBlank(responseUrl)) {
				response.sendRedirect(request.getContextPath() + responseUrl);
			}
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
				| SecurityException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}

}
